//
//  YLEmptyDataCollectionView.swift
//  Swift-Codedase
//
//  Created by xyanl on 2023/9/12.
//

import UIKit

///  A collection view  that allows to show easily placeholders like no results, no internet connection, etc
open class YLEmptyDataCollectionView: UICollectionView {
    
    // MARK: - Public properties
    
    /**
     * The layout used to organize the collected view’s items.
     Assigning a new layout object to this property causes the new layout to be applied (without animations) to the collection view’s items.
     */
    open override var collectionViewLayout: UICollectionViewLayout {
        didSet {
            if collectionViewLayout === placeholderLayout {
                return
            }
            defaultLayout = collectionViewLayout
        }
    }
    
    /// The placeholdersProvider property is responsible for the placeholders views and data
    final var provider = YLEmptyDataProvider.default {
        willSet {
            /// before changing the placeholders data, we should be sure that the collectionView is in the default configuration. Otherwise If the dataSource and the delegate are in placeholder configuration, and we set the new data, the old one will be released and we will lose the defaultDataSource and defaultDelegate (they will be set to nil)
            clear()
        }
    }
    
    /**
     * The object that acts as the delegate of the collection view placeholders.
     * The delegate must adopt the YLEmptyDataImplementDelegate protocol. The delegate is not retained.
     */
    weak var emptyDataDelegate: YLEmptyDataImplementDelegate?
    
    /**
     * The object that acts as the data source of the collection view.
     * The data source must adopt the UICollectionViewDataSource protocol. The data source is not retained.
     */
    open override weak var dataSource: UICollectionViewDataSource? {
        didSet {
            /* we save only the initial data source (and not a placeholder datasource) to allow to go back to the initial data */
            if  dataSource is YLEmptyDataImplement { return }
            defaultDataSource = dataSource
        }
    }
    
    /**
     * The object that acts as the delegate of the collection view.
     * The delegate must adopt the UICollectionViewDelegate protocol. The delegate is not retained.
     */
    open override weak var delegate: UICollectionViewDelegate? {
        didSet {
            /* we save only the initial delegate (and not the placeholder delegate) to allow to go back to the initial one */
            if  delegate is YLEmptyDataImplement {
                return
            }
            defaultDelegate = delegate
        }
    }
    
    /**
     * A Boolean value that determines whether bouncing always occurs when the placeholder is shown.
     * The default value is false
     */
    open var placeholdersAlwaysBounceVertical = false
    
    // MARK: - Private properties
    
    /// The defaultDataSource is used to allow to go back to the initial data source of the collection view after switching to a placeholder data source
    internal weak var defaultDataSource: UICollectionViewDataSource?
    
    /// The defaultDelegate is used to allow to go back to the initial delegate of the collection view after switching to a placeholder delegate
    internal weak var defaultDelegate: UICollectionViewDelegate?
    
    /// The defaultAlwaysBounceVertical is used to save the collectionView bouncing setup, because, when you switch to a placeholder, the vertical bounce is disabled
    fileprivate var defaultAlwaysBounceVertical: Bool!
    
    /// The defaultLayout is used to save the collectionView default layout setup, because, when you switch to a placeholder, the layout is changed to placeholderLayout
    fileprivate var defaultLayout: UICollectionViewLayout!
    
    /// The placeholderLayout used to show placeholder cell in the UICollectionView size
    fileprivate let placeholderLayout = UICollectionViewFlowLayout()
    
    // MARK: - init methods
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }
   
    public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        setup()
    }
    
    public init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout, delegate: AnyObject) {
        self.emptyDataDelegate = (delegate as! any YLEmptyDataImplementDelegate)
        super.init(frame: frame, collectionViewLayout: layout)
        self.dataSource = (delegate as! any UICollectionViewDataSource)
        self.delegate = (delegate as! any UICollectionViewDelegate)
        setup()
    }
    
    /**
     *  Config the collection view to be able to show placeholders
     */
    private func setup() {
        // register the placeholder view cell
        register(YLEmptyDataCollectionViewCell.self, forCellWithReuseIdentifier: "YLEmptyDataCollectionViewCell")
        
        defaultAlwaysBounceVertical = alwaysBounceVertical
        defaultLayout = collectionViewLayout
        
        emptyDataDelegate?.customEmptyDataProvider(self)
    }
    
    // MARK: - Manage collection view data and placeholders
    
    /**
     Switch to different data sources and delegate of the collection view (placeholders and initial data source & delegate)
     
     - parameter theSource:   the selected data source
     - parameter theDelegate: the selected delegate
     */
    internal func switchTo(dataSource theDataSource: UICollectionViewDataSource?, delegate theDelegate: UICollectionViewDelegate? = nil) {
        // if the data source and delegate are already set, no need to switch
        if dataSource === theDataSource && delegate === theDelegate {
            return
        }
        
        if theDataSource is YLEmptyDataImplement {
            // YLEmptyDataObject configuration
            alwaysBounceVertical = placeholdersAlwaysBounceVertical
            collectionViewLayout = placeholderLayout
        } else {
            // default configuration
            alwaysBounceVertical = defaultAlwaysBounceVertical
            collectionViewLayout = defaultLayout
        }
        dataSource = theDataSource
        delegate = theDelegate
        super.reloadData()
    }
    
    /// The total number of rows in all sections of the collectionView
    private func numberOfRowsInAllSections() -> Int {
        let numberOfSections = defaultDataSource?.numberOfSections?(in: self) ?? 1
        var rows = 0
        for i in 0 ..< numberOfSections {
            rows += defaultDataSource?.collectionView(self, numberOfItemsInSection: i) ?? 0
        }
        return rows
    }
    
    /**
     Reloads the rows and sections of the collection view.
     If the number of rows == 0 it shows no results placeholder
     */
    open override func reloadData() {
        // if the collectionView is empty we switch automatically to no data placeholder
        if numberOfRowsInAllSections() == 0 {
            emptyDataDelegate?.showEmptyDataForNoData!(in: self)
            return
        }
        // if the data source is in no data placeholder, and the user tries to reload data, we will switch automatically to default
        if dataSource is YLEmptyDataImplement {
            clear()
            return
        }
        super.reloadData()
    }
}

extension YLEmptyDataCollectionView {
    
    /// 显示占位符
    /// - Parameter data: 占位符data
    func showEmptyDataView(_ data: YLEmptyDataModel) {
        guard let dataSource = provider.dataSourceAndDelegate(with: data.key) else {
            assertionFailure("您的占位符提供程序配置不正确，没有找到带有键:\(String(describing: data.key))的占位符!")
            return
        }
        self.switchTo(dataSource: dataSource, delegate: dataSource)
    }
    
    /// 显示默认的
    public func clear() {
        self.switchTo(dataSource: defaultDataSource, delegate: defaultDelegate)
    }
    
}
